package generation;

import java.io.File;
import java.io.IOException;
import java.util.Date;

import javax.xml.XMLConstants;
import javax.xml.parsers.*;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.*;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.developpez.adiguba.shell.Shell;

import execution.*;

/**
 * InterpretationXML est la classe permettant d'une part de valider un fichier
 * xml selon le XSD, et d'autre part d'en extraire les informations permettant
 * de générer les instances de CampagneDeTest et de CasDeTest. Elle permet
 * ensuite, via des appels à genererCode, de préparer les codes correspondant à
 * l'étape de l'execution des tests.
 * 
 * 
 * 
 */
public class InterpretationXML {

	/**
	 * Constante indiquant le chemin vers le schéma Xml à utiliser
	 */
	private final String XSDFILEPATH = "faultinjector.xsd";

	/**
	 * Chemin vers le fichier xml décrivant une campagne
	 */
	private String xmlFilePath;

	/**
	 * Version DOM du fichier xml
	 */
	private Document xmlDoc;

	/**
	 * Instance de CampagneDeTest correspondant au xml
	 */
	private CampagneDeTest camp;

	/**
	 * Constructeur
	 * 
	 * @param _xmlFilePath
	 *            chemin vers le fichier xml
	 */
	public InterpretationXML(String _xmlFilePath) {
		xmlFilePath = _xmlFilePath;
	}

	public static void main(String[] args) {
		InterpretationXML x = new InterpretationXML("example.xml");
		try {
			x.isValid();
		} catch (SAXException e) {
		} catch (IOException e) {
		}
		System.out.println("ahahahahahahha");
	}

	/**
	 * Vérifie que le fichier xml fourni suit bien le schéma décrit dans le
	 * fichier XSD.
	 * 
	 * @return Boolean true si le xml est conforme au xsd, false sinon.
	 * @throws IOException 
	 * @throws SAXException 
	 */
	public Boolean isValid() throws SAXException, IOException {
		// parse un document XML en un arbre DOM
		DocumentBuilder parser;

		try {
			parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		} catch (ParserConfigurationException e1) {
			return false;
		}

		xmlDoc = parser.parse(new File(xmlFilePath));

		// créer un schemaFactory
		SchemaFactory factory = SchemaFactory
				.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

		// charge le fichier XSD
		Source schemaFile = new StreamSource(new File(XSDFILEPATH));
		Schema schema;
		try {
			schema = factory.newSchema(schemaFile);
		} catch (SAXException e1) {
			return false;
		}

		// créer un validateur
		Validator validator = schema.newValidator();

		// valide l'arbre DOM et donc le XML
		validator.validate(new DOMSource(xmlDoc));

		return true;
	}

	/**
	 * renvoie le chemin du fichier XSD utilisé
	 * 
	 * @return String
	 */
	public String getXsdFilePath() {
		return XSDFILEPATH;
	}

	/**
	 * Prépare la campagne de tests en instanciant les classes
	 * {@link CampagneDeTest} et {@link CasDeTest} en suivant la structure
	 * fournie par le xml.
	 * 
	 */
	public void preparerCampagne() {

		// Vérification de la conformité au xsd du fichier xml
		try {
			if (!isValid()) {
				return;
			}
		} catch (SAXException e) {
			return;
		} catch (IOException e) {
			return;
		}

		camp = new CampagneDeTest();

		NodeList tests = xmlDoc.getElementsByTagName("testCase");

		// Pour chaque noeud "testCase" du dom, on récupère le type, l'id, le
		// chemin du test et on détermine si les cas de test comprend des
		// observations Ocarina, GCC, LD et de variables. Enfin, on instancie la
		// classe CasDeTest avec les bons arguments.
		for (int i = 0; i < tests.getLength(); i++) {
			Node testCase = tests.item(i);

			int _type = Integer.parseInt(testCase.getAttributes().getNamedItem(
					"testType").getNodeValue());

			NodeList ocarinaTestCases = ((Element) testCase)
					.getElementsByTagName("observationOcarina");
			boolean _recuperationOcarina = (ocarinaTestCases.getLength() > 0) ? Boolean
					.parseBoolean(ocarinaTestCases.item(0).getTextContent())
					: false;

			NodeList gccTestCases = ((Element) testCase)
					.getElementsByTagName("observationGCC");
			boolean _recuperationGCC = (gccTestCases.getLength() > 0) ? Boolean
					.parseBoolean(gccTestCases.item(0).getTextContent())
					: false;

			NodeList ldTestCases = ((Element) testCase)
					.getElementsByTagName("observationLD");
			boolean _recuperationLD = (ldTestCases.getLength() > 0) ? Boolean
					.parseBoolean(ldTestCases.item(0).getTextContent()) : false;

			boolean _observerVariable = (((Element) testCase)
					.getElementsByTagName("observerVariable").getLength() > 0);

			String _nomVariable;
			int _numPartition;

			if (_observerVariable) {
				Node variable = ((Element) testCase).getElementsByTagName(
						"observerVariable").item(0);

				_nomVariable = ((Element) variable).getElementsByTagName(
						"varName").item(0).getTextContent();

				_numPartition = Integer.parseInt(((Element) variable)
						.getElementsByTagName("partitionNum").item(0)
						.getTextContent());
			} else {
				_nomVariable = null;
				_numPartition = 0;
			}

			String _idCas = testCase.getAttributes().getNamedItem("testId")
					.getNodeValue();
			String _chemin = null;

			CasDeTest cas = new CasDeTest(_idCas, _chemin, _type,
					_recuperationOcarina, _recuperationGCC, _recuperationLD,
					_observerVariable, _nomVariable, _numPartition);
			camp.ajouterCasDeTest(cas);
		}

	}

	/**
	 * genererCode copie les dossiers à modifier, puis appelle les classes
	 * GenererCode pour effetuer les remplacement de tags.
	 * 
	 * @see GenerateurCode
	 */
	public void genererCode() {

		NodeList tests = xmlDoc.getElementsByTagName("testCase");
		Shell sh = new Shell();

		for (int i = 0; i < tests.getLength(); i++) {
			Node testCase = tests.item(i);
			String destFileName = xmlDoc.getElementsByTagName("campaign").item(
					0).getAttributes().getNamedItem("campaignId")
					.getNodeValue()
					+ "-"
					+ testCase.getAttributes().getNamedItem("testId")
							.getNodeValue() + "-" + (new Date().getTime());
			String projectFromLocation = ((Element) testCase)
					.getElementsByTagName("projectLocation").item(0)
					.getTextContent();

			String pokPath = xmlDoc.getElementsByTagName("pokPath").item(0)
					.getTextContent();

			// On définit le nom du dossier dans lequel on effectuera les tests:
			// au même niveau que le dossier d'origine
			// Dans le dossier contenant les cas de test. Nom = Nom campagne +
			// nom test + nombre pseudo-aléatoire(date en ms);
			String projectLocation = pokPath
					+ (pokPath.charAt(pokPath.length() - 1) != "/".charAt(0) ? "/"
							: "") + destFileName + "/";

			try {
				sh.command("rm -rf " + projectLocation).consume();
				sh.command(
						"cp -r " + projectFromLocation + " " + projectLocation)
						.consume();
			} catch (IOException e) {
				e.printStackTrace();
			}

			GenerateurCode gen = null;
			camp.getCampagne().get(i).setCheminRepertoire(projectLocation);

			int type = Integer.parseInt(testCase.getAttributes().getNamedItem(
					"testType").getNodeValue());

			switch (type) {
			case 0:
				gen = new GenerateurModeleAADL();
				break;
			case 1:
				gen = new GenerateurCodeC();
				break;
			default:
			}
			gen.genererCode(xmlDoc, projectLocation, i);

		}
	}

	/**
	 * Retourne l'instance de {@link CampagneDeTest}
	 * 
	 * @return
	 */
	public CampagneDeTest getCamp() {
		return camp;
	}

}
